﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Configuration;

namespace EcodexIntegrationSample
{
    public class Certificado
    {
        public static String PathCertificados
        {
            get
            {
                return ConfigurationManager.AppSettings["CertFolder"];
            }
        }

        static String PathOpenSSl
        {
            get
            {
                return ConfigurationManager.AppSettings["sslFolder"];
            }
        }

        public static string GetCertificado(String rfc, String noCertificado)
        {
            string CertId = GetCertId(rfc, noCertificado);
            var certEmisor = ReadCert(CertId, true);
            return Convert.ToBase64String(certEmisor.GetRawCertData());
        }

        public static void CargarCertificado(string rfc, string PathCer, String PathKey, String Pwd)
        {
            String FileNameCer = String.Empty;
            String FileNamePem = String.Empty;
            String strCertificado = PathCer;


            //Verifica la existencia de los archivos
            if (!File.Exists(PathCer))
            {
                throw new Exception("No se encontro el archivo .cer");
            }

            if (!File.Exists(PathKey))
            {
                throw new Exception("No se encontro el archivo .key");
            }

            try
            {
                X509Certificate2 certEmisor = new X509Certificate2();
                byte[] byteCertData = ReadFile(strCertificado);
                certEmisor.Import(byteCertData);
                string noCertificado = GetNoCertificado(certEmisor.SerialNumber);

                FileNameCer = String.Format("{0}{1}.cer", PathCertificados, GetCertId(rfc, noCertificado));
                FileNamePem = Path.GetFullPath(String.Format("{0}{1}.pem", PathCertificados, GetCertId(rfc, noCertificado)));
            }
            catch (Exception ex)
            {
                throw new Exception("El archivo .cer no es un certificado valido", ex);
            }

            try
            {
                if (File.Exists(FileNameCer))
                    File.Delete(FileNameCer);

                File.Copy(PathCer, FileNameCer);

                if (!File.Exists(FileNamePem))
                    File.Delete(FileNamePem);
            }
            catch (Exception)
            {
                throw new Exception("Error al cargar el certificado");
            }

            String ConvertirKey = String.Format("/C {0}openssl pkcs8 -inform DER -in \"{1}\" -passin pass:{2} >\"{3}\"",
                PathOpenSSl, PathKey, Pwd, FileNamePem);
            Utils.Cmd.Ejecuta(ConvertirKey);

            if (!File.Exists(FileNamePem))
                throw new Exception("Error al cargar el certificado. Verifique el archivo y la contraseña de la llave");
        }

        private static String GetNoCertificado(String SerialNumber)
        {
            var noCertificado = String.Empty;
            for (int i = 0; i <= SerialNumber.Length; i++)
            {
                if (i % 2 != 0)
                    noCertificado += SerialNumber.Substring(i, 1);
            }
            return noCertificado;
        }

        internal static string GetCertId(String rfc, String noCertificado)
        {
            String FileName = String.Empty;
            if (rfc.Length == 12)
            {
                FileName = String.Format("{0}_{1}", rfc.Substring(3, 6), noCertificado);
            }
            else
            {
                FileName = String.Format("{0}_{1}", rfc.Substring(4, 6), noCertificado);
            }
            return FileName;
        }

        private static X509Certificate2 ReadCert(String PathCertificado)
        {
            if (!File.Exists(PathCertificado))
                throw new Exception(String.Format("No existe el certificado con ese rfc y no. de certificado en el repositorio "));

            try
            {
                X509Certificate2 certEmisor = new X509Certificate2();
                byte[] byteCertData = ReadFile(PathCertificado);
                certEmisor.Import(byteCertData);
                return certEmisor;
            }
            catch (Exception ex)
            {
                throw new Exception("Error al leer el certificado", ex);

            }
        }

        private static X509Certificate2 ReadCert(String IDCertificado, bool buscar = true)
        {
            String strCertificado = String.Format("{0}{1}.cer", PathCertificados, IDCertificado);
            return ReadCert(strCertificado);
        }

        static byte[] ReadFile(string filePath)
        {
            byte[] buffer;
            FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            try
            {
                int length = (int)fileStream.Length;  // get file length
                buffer = new byte[length];            // create buffer
                int count;                            // actual number of bytes read
                int sum = 0;                          // total number of bytes read

                // read until Read method returns 0 (end of the stream has been reached)
                while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                    sum += count;  // sum is a buffer offset for next reading
            }
            finally
            {
                fileStream.Close();
            }
            return buffer;
        }
    }
}
